package com.syg.spring;

import java.io.File;
import java.net.URL;

/**
 * @author ps
 */
public class SygConfigApplicationContext {

    /**
     * 4,因为要接收spring容器传进来的配置,所以我们需要创建一个属性接收
     */
    private Class configClass;

    public SygConfigApplicationContext(Class configClass) {
        this.configClass = configClass;
        /**
         * 13,扫描
         * 那spring怎么知道扫描路径在哪里？就在构造方法的入参上,因为我们把配置类定义在上配置类上
         */
        // 14,判断配置类上有木有ComponentScan的注解,有的话,获取配置的类路径
        if (configClass.isAnnotationPresent(ComponentScan.class)) {
            // 15,一个类上可能有很多注解,此时我们只关心@ComponentScan,所以我只获取ComponentScan的信息
            ComponentScan componentScan = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
            // 16,获取注解上配置的扫描路径, 实际上获取的是com.syg.biz(因为配置的就是这玩意)
            String scanPath = componentScan.value();
            scanPath = scanPath.replace(".", "/");
            // 17,扫描到底扫描的是什么呢？实际上扫描的是.class,那.class文件的位置在哪里呢？自然是编译以后从classpath获取
            ClassLoader classLoader = SygConfigApplicationContext.class.getClassLoader();
            // 18,获取相对路径下的资源(如果含有中文会出错,此处证明了尽量少用中文)
            URL resource = classLoader.getResource(scanPath);
            // 19,resource.getFile()就可以获取到当前的绝对路径(此时就获取到了绝对路径:D:/**/**/**)
            String resFile = resource.getFile();
            // 为了临时解决中文乱码问题,实际上应该直接传入resource.getFile()
            resFile = System.getProperty("user.dir")+"\\SpringBoot\\Spring：Spring源码研究\\Spring：手写Spring\\_01_Spring_ComponentScan\\out\\production\\classes\\"+scanPath;
            File scanFile = new File(resFile);
            // 测试一下System.out.println(scanFile);
            if (scanFile.isDirectory()) {
                // 获取文件夹下的所有对象
                File[] files = scanFile.listFiles();
                for (File file : files) {
                    String fileName = file.getAbsoluteFile().getName();
                    // 测试一下System.out.println(file.getName());
                    if (fileName.endsWith(".class")) {
                        /*
                         * 只关心.class的文件,
                         * 20,重点   那怎么判断.class文件是不是一个bean呢？答：判断有木有@componpent
                         * 那怎么判断一个类是上有木有@componpent注解呢？
                         * 答：用反射,要用反射就需要拿到class对象
                         * 怎么拿呢？用类加载器传入类的全限定名
                         */
                        String className = fileName.substring(fileName.indexOf("com", fileName.indexOf(".class")));
                        className = className.replace("\\", ",");
                        // 此时className就是类的全限定名
                        try {
                            Class<?> clazz = classLoader.loadClass(className);
                            // 20-1 判断类上有木有@componpent注解
                            if (clazz.isAnnotationPresent(Component.class)) {
                                /*
                                 * 那么此处就要创建bean对象嘛？
                                 * 如果此时创建对象,有很多问题：
                                 *      1,单例、多例怎么处理？
                                 *      2,bean对象怎么扩展？
                                 *      3,getBean()方法怎么处理？等等一系列的问题
                                 * 那spring怎么处理的呢?给所有的bean定义信息封装一下,叫BeanDefinition吧
                                 */
                                BeanDefinition beanDefinition = new BeanDefinition();
                                beanDefinition.setType(clazz);
                                if (clazz.isAnnotationPresent(Scope.class)) {
                                    Scope scope = clazz.getAnnotation(Scope.class);
                                    beanDefinition.setScope(scope.value());
                                }else{
                                    // 默认单例
                                    beanDefinition.setScope("singleton");
                                }
                                //

                            }
                        } catch (ClassNotFoundException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }

    }

    /**
     * 12,获取bean对象,此时我们无法知道返回对象的类型,所以返回Object
     * 那么入参是什么呢？我们通常是根据bean的名字获取对象,所以入参就是beanName
     *
     *
     * 衔接点：那么此时怎么获取对象呢？头绪肯定是通过配置的类路径+反射创建一个对象。
     * 既然这样我们就在getBean()中利用获得路径+反射创建对象。
     *
     * 这样带来一个问题,每次获取bean对象时,我们都需要扫描一次类路径,性能太低了,
     *
     * 那我们还能怎么做呢？
     * 答：在类初始化时创建bean
     */
    public Object getBean(String beanName) {
        return null;
    }
}
